'''
作者: 工控万金油
首发日期: 2025-08-01
更新日期: 2025-08-05
版本: 1.0.2
声明: 我的代码基于CustomTkinter框架
'''
from PIL import Image
import public_lib as plib ,os
import zipPicture_ui
import threading as thd
import json
LOG_NAME = "LOG.log"
plib.run_path(__file__)
def compress_image(input_image_path, output_image_path, quality=30,new_size=1):
    with Image.open(input_image_path) as image:
        image.getexif()  # 获取图片的exif信息
        # image.thumbnail((299, 299))  # 设置图片的新尺寸大小
        icc_profile = image.info.get("icc_profile")  # 获取原图ICC配置文件
        # 将EXIF数据写回图像文件
        exif_bytes = image.getexif().tobytes()
        # 获取图片的原始大小
        original_size = image.size
        # 计算等比例缩放后的大小
        ratio = new_size
        new_size = (int(original_size[0] * ratio), int(original_size[1] * ratio))
        # 等比例缩放图片
        resized_img = image.resize(new_size, Image.Resampling.LANCZOS)

        img_type = input_image_path.split('.')[-1]
        if img_type in ['jpg','JPG','jpeg','JPEG']:
            img_type = 'jpeg' 
        if img_type in ['jpg','JPG','webp','WEBP','jpeg','JPEG'] :
            save_kwargs = {
                'exif':exif_bytes, # 写入EXIF数据
                'quality':quality, # 设置压缩质量
                'optimize':True,    # 优化压缩
                'icc_profile':icc_profile, # 写入ICC配置文件
                'format':img_type,
                }
        else:
            if resized_img.mode == 'RGBA':
                resized_img = resized_img.convert('RGB')
            output_image_path = output_image_path.replace(f'.{img_type}',f'.jpg') # png转jpg，路径改后缀
            save_kwargs = {
                'exif':exif_bytes, # 写入EXIF数据
                'quality':quality, # 设置压缩质量
                'optimize':True,    # 优化压缩
                'format':'jpeg' if resized_img.mode == 'RGB' else img_type,
                'reduce_colors':True,
                'icc_profile':icc_profile, # 写入ICC配置文件
                }

        resized_img.save(output_image_path, # 保存压缩后的图片
                          **save_kwargs
                          )  # 保存图片
# 单图压缩
def zip_click_img(event):
    if app.file_path != '':
        folderName = plib.NowTime(1)                # 压缩后的文件夹名
        tmpFileName = app.file_path.split('/')[-1]  # 压缩前的文件名
        plib.exists_folder(f'{folderName}')         # 创建文件夹
        try:
            compress_image(app.file_path, 
                           f'{folderName}/{tmpFileName}',
                             quality=app.img_quality_get,  # 质量
                             new_size=app.img_new_size_get # 尺寸缩放比
                             )
            msgbox =  f"图片已压缩，并存放在程序根目录的【{folderName}】目录下。"
        except Exception as e:
            msgbox =  f"图片{app.file_path}压缩出错！"
            plib.info(LOG_NAME,0,f'文件{app.file_path}，压缩出错',e)
        zipPicture_ui.messagebox.showinfo("提示",msgbox)   # 压缩完成弹出提示
def zip_click_folder(event):

    if app.folder_path_list != []:
        app.many_img_size_list[13].configure(progress_color= ("#198bce","#198bce"))
        # app.many_img_size_list[13].start()
        thd0 = thd.Thread(target=process_bar)
        thd0.daemon = True
        thd0.start()

    print(event)
def process_bar():
    err_list  = []                              # 处理错误的文件名存放列表
    folderName = plib.NowTime(1)                # 压缩后的文件夹名
    plib.exists_folder(f'{folderName}')         # 创建文件夹
    for num,path in enumerate(app.folder_path_list):
        tmpFolderName_s = path.split(os.path.basename(path))[0]       # 只取文件夹路径
        tmp_mkdir_folder = tmpFolderName_s.split(app.folder_path)[-1] # 
        tmp_fileName = path.split(tmpFolderName_s)[-1]
        if tmpFolderName_s != '\\':
            plib.exists_folder(f'{folderName}{tmp_mkdir_folder}')     # 创建文件夹
        try:
            compress_image(path, 
                           f'{folderName}/{tmp_mkdir_folder}/{tmp_fileName}', 
                           quality=app.many_img_quality_get,
                           new_size=app.many_img_new_size_get
                           )
            msgbox =  f"图片已压缩，并存放在程序根目录的【{folderName}】目录下。"
        except Exception as e:
            err_list.append(app.file_path)
            plib.info(LOG_NAME,0,f'文件{app.file_path}，压缩出错',e)
        if err_list != []:
            msgbox = f"图片已压缩，并存放在程序根目录的【{folderName}】目录下。但有{len(err_list)}个文件压缩失败！具体压缩出错文件详见日志文件。"
        num += 1
        app.many_img_size_list[22].configure(text = f"正在压缩第{num}张图片...")  # ui正常处理文本显示
        pcb = (num+1)/len(app.folder_path_list)*1                                 # 进度数值0~1
        app.many_img_size_list[13].set(pcb)                                       # 进度条显示
    zipPicture_ui.messagebox.showinfo("提示", msgbox)                             # 压缩完成弹出提示
    # 进度条恢复初始状态---
    app.many_img_size_list[13].set(0)
    app.many_img_size_list[13].configure(progress_color= ("#fcfcfc","#8d8d8d"))
    app.many_img_size_list[22].configure(text = f"")
# 写入配置到json文件（config.conf)
def WD_file(nfile, data):
    with open(nfile,'w',encoding='utf-8') as f:
        temp = json.dumps(data,indent=1,ensure_ascii=False)
        f.write(temp)
if __name__ == '__main__':
    try:
        app = zipPicture_ui.App()       # 窗口类
        app.title("图片压缩V1.0.2")     # 标题
        wh=app.winfo_screenheight()     # 获取屏幕高度
        ww=app.winfo_screenwidth()      # 获取屏幕宽度
        mwh=450                         # 窗口高度
        mww=700                         # 窗口宽度
        x=(ww-mww)/2
        y=(wh-mwh)/3
        app.geometry("%dx%d+%d+%d"%(mww,mwh,x,y))       #窗口水平居中偏上显示   
        # 读取配置参数
        try:
            with open('config.conf', 'r', encoding='utf-8') as f:
                config = eval(f.read())
            app.img_quality_get = config['img_quality_get']              # 单图压缩质量
            app.img_new_size_get = config['img_new_size_get']            # 单图压缩尺寸
            app.many_img_quality_get = config['many_img_quality_get']    # 多图压缩质量   
            app.many_img_new_size_get = config['many_img_new_size_get']  # 多图压缩尺寸
            img_quality = app.img_quality_get
            img_new_size = int(app.img_new_size_get*100)
            many_img_quality = app.many_img_quality_get
            many_img_new_size = int(app.many_img_new_size_get*100)
            theme_mode = config['theme_mode']
        except:
            img_quality = 30
            img_new_size = 100
            many_img_quality = 30
            many_img_new_size = 100
            theme_mode = '浅色主题'
        # 主题模式
        appearance_mode = {"浅色主题":"light","深色主题":"dark","跟随系统":"system"}
        zipPicture_ui.customtkinter.set_appearance_mode(appearance_mode[theme_mode])
        app.appearance_mode_menu.set(theme_mode)

        # 设置压缩质量和尺寸默认值
        # 单图压缩窗口
        ratio_cmp = {30:5,60:4,90:3}
        size_cmp = {30:11,60:10,90:9}
        app.set_Entry_value(app.img_size_list[8], img_quality)
        app.switch_button_color(app.img_size_list[8], 
                                img_quality,
                                app.img_size_list,
                                size_cmp[img_quality] if img_quality in size_cmp else 101)
        app.set_Entry_value(app.img_size_list[2], img_new_size)
        app.switch_button_color(app.img_size_list[2],
                            img_new_size,
                            app.img_size_list,
                            ratio_cmp[img_new_size] if img_new_size in ratio_cmp else 100)     
        # 多图压缩窗口
        app.set_Entry_value(app.many_img_size_list[8], many_img_quality)
        app.switch_button_color(app.many_img_size_list[8],
                                many_img_quality,
                                app.many_img_size_list,
                                size_cmp[many_img_quality] if many_img_quality in size_cmp else 101)
        app.set_Entry_value(app.many_img_size_list[2], many_img_new_size)
        app.switch_button_color(app.many_img_size_list[2],
                            many_img_new_size,
                            app.many_img_size_list,
                            ratio_cmp[many_img_new_size] if many_img_new_size in ratio_cmp else 100)       
        # 设置按钮事件
        app.img_size_list[14].bind("<Button-1>", zip_click_img)          # 单图压缩按钮绑定方法
        app.many_img_size_list[14].bind("<Button-1>", zip_click_folder)  # 多图压缩按钮绑定方法    
        app.mainloop()
    finally:
        # 程序退出时保存配置参数
        theme_mode = app.appearance_mode_menu.get()
        config_dict = {'img_quality_get':app.img_quality_get,
                       'img_new_size_get':app.img_new_size_get,
                       'many_img_quality_get':app.many_img_quality_get,
                       'many_img_new_size_get':app.many_img_new_size_get,
                       'theme_mode':theme_mode
                       }
        # 写入配置文件
        WD_file('config.conf',config_dict)
